package jsr292.java.lang.invoke;

class Conversions {
    private static int pureHash(Class<?> clazz) {
        String name=clazz.getName();
        //assert name.length()>=2; 
        return name.charAt(0) << 8 + name.charAt(1);
    }

    static Object convert(Class<?> from, Class<?> to, Object value) {
        //assert from==Object.class || from.isPrimitive() || from==void.class;
        //assert to==Object.class || to.isPrimitive() || to==void.class;

        //System.out.println("from "+from+" to "+to+" value "+value);
        
        if (from == to)
            return value;

        switch(pureHash(from)) {
        case 'b'<<8+'o': // boolean
            return convertFromInteger(to, ((Boolean)value)?1:0);
        case 'b'<<8+'y': // byte
            return convertFromInteger(to, (Byte)value);
        case 'c'<<8+'h': // char
            return convertFromInteger(to, (Character)value);
        case 'd'<<8+'o': // double
            return convertFromDouble(to, (Double)value);
        case 'f'<<8+'l': // float
            return convertFromFloat(to, (Float)value);
        case 'i'<<8+'n': // int
            return convertFromInteger(to, (Integer)value);
        case 'l'<<8+'o': // long
            return convertFromLong(to, (Long)value);
        case 's'<<8+'h': // short
            return convertFromInteger(to, (Character)value);
        case 'j'<<8+'a': // Object (java/lang/Object)
            // Object/boxed/null to Object conversion
            return value;
        case 'v'<<8+'o': // void
            return convertFromVoid(to);
        default:
            throw new AssertionError("no decoder for "+from);
        }
    }

    private static Object convertFromDouble(Class<?> to, double value) {
        switch(pureHash(to)) {
        case 'b'<<8+'o': // boolean  //XXX what does it exactly mean ?
            return (value==0)?Boolean.FALSE:Boolean.TRUE;
        case 'b'<<8+'y': // byte
            return (byte)value;
        case 'c'<<8+'h': // char
            return (char)value;
        case 'j'<<8+'a': // Object (java/lang/Object)
            return value;
        case 'f'<<8+'l': // float
            return (float)value;
        case 'i'<<8+'n': // int
            return (int)value;
        case 'l'<<8+'o': // long
            return (long)value;
        case 's'<<8+'h': // short
            return (short)value;
        default:
            throw new AssertionError("no encoder for "+to);
        }
    }

    private static Object convertFromFloat(Class<?> to, float value) {
        switch(pureHash(to)) {
        case 'b'<<8+'o': // boolean  //XXX what does it exactly mean ?
            return (value==0)?Boolean.FALSE:Boolean.TRUE;
        case 'b'<<8+'y': // byte
            return (byte)value;
        case 'c'<<8+'h': // char
            return (char)value;
        case 'd'<<8+'o': // double
            return (double)value;
        case 'j'<<8+'a': // Object (java/lang/Object)
            return value;
        case 'i'<<8+'n': // int
            return (int)value;
        case 'l'<<8+'o': // long
            return (long)value;
        case 's'<<8+'h': // short
            return (short)value;
        default:
            throw new AssertionError("no encoder for "+to);
        }
    }

    private static Object convertFromLong(Class<?> to, long value) {
        switch(pureHash(to)) {
        case 'b'<<8+'o': // boolean
            return (value==0)?Boolean.FALSE:Boolean.TRUE;
        case 'b'<<8+'y': // byte
            return (byte)value;
        case 'c'<<8+'h': // char
            return (char)value;
        case 'd'<<8+'o': // double
            return (double)value;
        case 'f'<<8+'l': // float
            return (float)value;
        case 'i'<<8+'n': // int
            return (int)value;
        case 'j'<<8+'a': // Object (java/lang/Object)
            return value;
        case 's'<<8+'h': // short
            return (short)value;
        default:
            throw new AssertionError("no encoder for "+to);   
        }
    }

    private static Object convertFromInteger(Class<?> to, int value) {
        switch(pureHash(to)) {
        case 'b'<<8+'o': // boolean
            return (value==0)?Boolean.FALSE:Boolean.TRUE;
        case 'b'<<8+'y': // byte
            return (byte)value;
        case 'c'<<8+'h': // char
            return (char)value;
        case 'd'<<8+'o': // double
            return (double)value;
        case 'f'<<8+'l': // float
            return (float)value;
        case 'j'<<8+'a': // Object (java/lang/Object)
        case 'i'<<8+'n': // int   because of implicit (boolean -> int)
            return value;
        case 'l'<<8+'o': // long
            return (long)value;
        case 's'<<8+'h': // short
            return (short)value;
        default:
            throw new AssertionError("no encoder for "+to);
        }
    }

    private static Object convertFromVoid(Class<?> to) {
        switch(pureHash(to)) {
        case 'b'<<8+'o': // boolean
            return false;
        case 'b'<<8+'y': // byte
            return (byte)0;
        case 'c'<<8+'h': // char
            return (char)0;
        case 'd'<<8+'o': // double
            return 0.0;
        case 'f'<<8+'l': // float
            return 0.0f;
        case 'i'<<8+'n': // int
            return 0;
        case 'j'<<8+'a': // Object (java/lang/Object)
            return null;
        case 'l'<<8+'o': // long
            return (long)0;
        case 's'<<8+'h': // short
            return (short)0;
        default:
            throw new AssertionError("no encoder for "+to);
        }
    }

    static boolean needConversion(Class<?> from, Class<?> to) {
        if (from==to)
            return false;

        return from==void.class || to==void.class ||
               (from.isPrimitive() && to.isPrimitive());
    }

    static boolean needConversions(MethodType fromType, MethodType toType, int argumentCount) {
        if (needConversion(fromType.returnType, toType.returnType))
            return true;

        for(int i=0; i<argumentCount; i++) {
            if (needConversion(toType.parameterType(i), fromType.parameterType(i)))
                return true;
        }
        return false;
    }
}
